package com.fy.fyspace.common.utils;

import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import com.baomidou.mybatisplus.generator.function.ConverterFileName;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * MyBatis-Plus 代码生成器
 * @author fwq
 * @date 2024-03-19
 */
public class CodeGenerator {
    // 数据库连接配置
    private static final String JDBC_URL = "jdbc:mysql://localhost:3306/fyspace?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true&useSSL=false&allowPublicKeyRetrieval=true";
    private static final String JDBC_USERNAME = "root";
    private static final String JDBC_PASSWORD = "fwq4221@";

    // 包名和模块名
    private static final String PACKAGE_NAME = "com.generator";
    private static final String MODULE_NAME = "";

    /**
     * 获取数据库中所有表名
     */
    private static List<String> getAllTables() throws Exception {
        List<String> tables = new ArrayList<>();
        Class.forName("com.mysql.cj.jdbc.Driver");

        try (Connection conn = DriverManager.getConnection(JDBC_URL, JDBC_USERNAME, JDBC_PASSWORD);
             Statement stmt = conn.createStatement()) {
            Pattern pattern = Pattern.compile("jdbc:mysql://[^/]+/([^?]+)");
            Matcher matcher = pattern.matcher(JDBC_URL);
            String dbName = matcher.find() ? matcher.group(1) : "";

            if (dbName.isEmpty()) {
                throw new Exception("无法从URL中解析数据库名");
            }

            ResultSet rs = stmt.executeQuery("SELECT table_name FROM information_schema.tables WHERE table_schema = '" + dbName + "' AND table_type = 'BASE TABLE'");
            while (rs.next()) {
                tables.add(rs.getString(1));
            }
        }
        return tables;
    }

    /**
     * 转换表名为规范的驼峰命名
     */
    private static String convertToCamelCase(String tableName) {
        StringBuilder result = new StringBuilder();
        String[] words = tableName.toLowerCase().split("_");

        for (String word : words) {
            if (word.isEmpty()) continue;

            // 特殊单词处理
            switch (word) {
                case "feedback":
                    result.append("FeedBack");
                    break;
                case "info":
                    result.append("Info");
                    break;
                case "id":
                    result.append("ID");
                    break;
                case "url":
                    result.append("URL");
                    break;
                default:
                    result.append(Character.toUpperCase(word.charAt(0)))
                            .append(word.substring(1).toLowerCase());
            }
        }

        return result.toString();
    }

    public static void main(String[] args) {
        try {
            // 获取所有表名
            List<String> tables = getAllTables();
            if (tables.isEmpty()) {
                System.out.println("数据库中没有找到任何表！");
                return;
            }
            System.out.println("找到以下表：" + String.join(", ", tables));

            // 项目根路径
            String projectPath = System.getProperty("user.dir");

            // 创建文件名转换器
            ConverterFileName entityNameConverter = tableName -> convertToCamelCase(tableName);
            ConverterFileName mapperNameConverter = tableName -> convertToCamelCase(tableName) + "Mapper";
            ConverterFileName serviceNameConverter = tableName -> "I" + convertToCamelCase(tableName) + "Service";
            ConverterFileName serviceImplNameConverter = tableName -> convertToCamelCase(tableName) + "ServiceImpl";
            ConverterFileName controllerNameConverter = tableName -> convertToCamelCase(tableName) + "Controller";

            // 1、配置数据源
            FastAutoGenerator.create(JDBC_URL, JDBC_USERNAME, JDBC_PASSWORD)
                    // 2、全局配置
                    .globalConfig(builder -> {
                        builder.author("fwq") // 设置作者名称
                                .dateType(DateType.ONLY_DATE) // 时间策略
                                .commentDate("yyyy-MM-dd") // 注释日期
                                .outputDir(projectPath + "/src/main/java") // 输出目录
                                .fileOverride() // 覆盖已有文件
                                .disableOpenDir() // 禁止打开输出目录
                                .enableSwagger() // 开启 swagger 模式
                                .enableSpringdoc() // 开启 springdoc 模式
                                .build();
                    })
                    // 3、包配置
                    .packageConfig(builder -> {
                        builder.parent(PACKAGE_NAME) // 父包名
                                .moduleName(MODULE_NAME) // 模块名
                                .entity("model.entity") // 实体类包名
                                .service("service") // service包名
                                .serviceImpl("service.impl") // serviceImpl包名
                                .mapper("mapper") // mapper包名
                                .controller("controller") // controller包名
                                .pathInfo(Collections.singletonMap(
                                        OutputFile.xml,
                                        projectPath + "/src/main/resources/mapper")) // mapper.xml的输出路径
                                .build();
                    })
                    // 4、策略配置
                    .strategyConfig(builder -> {
                        builder.addInclude(tables) // 设置需要生成的表名
                                // 4.1、Entity策略配置
                                .entityBuilder()
                                .enableLombok() // 开启lombok
                                .enableTableFieldAnnotation() // 开启生成实体时生成字段注解
                                .naming(NamingStrategy.no_change) // 不使用默认的命名转换，使用自定义的
                                .columnNaming(NamingStrategy.no_change)
                                .enableRemoveIsPrefix() // 开启 Boolean 类型字段移除 is 前缀
                                .convertFileName(entityNameConverter) // 使用自定义的命名转换
                                .build()
                                // 4.2、Controller策略配置
                                .controllerBuilder()
                                .enableRestStyle() // 开启生成@RestController
                                .convertFileName(controllerNameConverter) // 格式化文件名称
                                .enableHyphenStyle() // 开启驼峰转连字符
                                .build()
                                // 4.3、Service策略配置
                                .serviceBuilder()
                                .convertServiceFileName(serviceNameConverter) // 格式化 service 接口文件名称
                                .convertServiceImplFileName(serviceImplNameConverter) // 格式化 service 实现类文件名称
                                .build()
                                // 4.4、Mapper策略配置
                                .mapperBuilder()
                                .enableMapperAnnotation() // 开启 @Mapper 注解
                                .enableBaseResultMap() // 启用 BaseResultMap 生成
                                .enableBaseColumnList() // 启用 BaseColumnList
                                .convertMapperFileName(mapperNameConverter) // 格式化 mapper 文件名称
                                .convertXmlFileName(mapperNameConverter) // 格式化 xml 文件名称
                                .build();
                    })
                    // 5、模板引擎配置
                    .templateEngine(new FreemarkerTemplateEngine())
                    // 6、执行
                    .execute();

            System.out.println("代码生成完成！");
        } catch (Exception e) {
            System.out.println("代码生成失败：" + e.getMessage());
            e.printStackTrace();
        }
    }
}